package com.andova.net.upload;

import android.app.Activity;
import android.graphics.Bitmap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.andova.util.ObserverFunc2;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import org.jetbrains.annotations.NotNull;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

import static com.andova.toolkit.util.ImageUtil.*;

/**
 * Created by Administrator on 2017-10-09.
 *
 * @author kzaxil
 * @since 1.0.0
 */
@SuppressWarnings({"WeakerAccess", "unused", "BooleanMethodIsAlwaysInverted"})
public abstract class BaseUploadFile {
    private int sign = -1;
    private boolean lock;
    private UploadBean originBean;
    private List<UploadBean> originUploadList = new ArrayList<>();
    private List<UploadBean> remoteUploadList = new ArrayList<>();

    private int flag;
    private String id;
    private int maxKB;
    private int targetWidth;
    private int targetHeight;
    private boolean compress;

    private final int FLAG_DEFAULT = 0;

    /**
     * 用来判断是否需要上传，如果标志位为已上传状态，则不需要上传
     */
    private boolean judgeUpdate(UploadBean uploadBean, int flag) {
        this.flag = flag;
        if (lock) {
            return false;
        }
        lock = true;
        if (uploadBean.updated) {
            uploadComplete(uploadBean, uploadBean, flag);
            return false;
        }
        uploadReady();
        if (sign == -1) { // 表示是单个文件上传
            remoteUploadList.clear();
        }
        originBean = uploadBean;
        return true;
    }

    /**
     * 用来判断是否需要上传，如果标志位为已上传状态，则不需要上传
     */
    private boolean judgeUpdate(List<UploadBean> uploadList, int flag) {
        if (uploadList == null || uploadList.size() == 0) return false;
        if (lock) {
            return false;
        }
        remoteUploadList.clear();
        originUploadList = uploadList;
        for (UploadBean uploadBean : uploadList) {
            if (!uploadBean.updated) continue;
            remoteUploadList.add(uploadBean);
            originUploadList.remove(uploadBean);
        }
        if (originUploadList.size() == 0) {
            uploadComplete(originUploadList, remoteUploadList, flag);
            return false;
        }
        return true;
    }

    public void upload(List<UploadBean> uploadList, int flag) {
        if (!judgeUpdate(uploadList, flag)) return;

        this.flag = flag;
        sign = originUploadList.size() - 1;
        upload(originUploadList.get(sign), flag);
    }

    public void upload(UploadBean uploadBean, int flag) {
        if (uploadBean == null) return;

        if (!judgeUpdate(uploadBean, flag)) return;

        upload(uploadBean);
    }

    public void uploadImageWithFlag(List<UploadBean> uploadList, boolean compress,
                                    int targetWidth, int targetHeight,
                                    int maxKB, int flag) {
        if (!judgeUpdate(uploadList, flag)) return;

        this.flag = flag;
        this.maxKB = maxKB;
        this.compress = compress;
        this.targetWidth = targetWidth;
        this.targetHeight = targetHeight;
        sign = originUploadList.size() - 1;
        uploadImageWithFlag(originUploadList.get(sign), compress, targetWidth, targetHeight, maxKB, flag);
    }

    public void uploadImage(Bitmap bitmap) {
        uploadImageWithFlag(bitmap, FLAG_DEFAULT);
    }

    public void uploadImage(UploadBean uploadBean) {
        uploadImageWithFlag(uploadBean, FLAG_DEFAULT);
    }

    public void uploadImageWithFlag(Bitmap bitmap, int flag) {
        uploadImageWithFlag(bitmap, false, -1, -1, -1, flag);
    }

    public void uploadImageWithFlag(UploadBean uploadBean, int flag) {
        uploadImageWithFlag(uploadBean, false, -1, -1, -1, flag);
    }

    public void uploadImage(Bitmap bitmap, int maxKB) {
        uploadImageWithFlag(bitmap, maxKB, FLAG_DEFAULT);
    }

    public void uploadImage(UploadBean uploadBean, int maxKB) {
        uploadImageWithFlag(uploadBean, maxKB, FLAG_DEFAULT);
    }

    public void uploadImageWithFlag(Bitmap bitmap, int maxKB, int flag) {
        uploadImageWithFlag(bitmap, true, -1, -1, maxKB, flag);
    }

    public void uploadImageWithFlag(UploadBean uploadBean, int maxKB, int flag) {
        uploadImageWithFlag(uploadBean, true, -1, -1, maxKB, flag);
    }

    public void uploadImage(Bitmap bitmap, int targetWidth, int targetHeight) {
        uploadImageWithFlag(bitmap, targetWidth, targetHeight, FLAG_DEFAULT);
    }

    public void uploadImage(UploadBean uploadBean, int targetWidth, int targetHeight) {
        uploadImageWithFlag(uploadBean, targetWidth, targetHeight, FLAG_DEFAULT);
    }

    public void uploadImageWithFlag(Bitmap bitmap, int targetWidth, int targetHeight, int flag) {
        uploadImageWithFlag(bitmap, true, targetWidth, targetHeight, -1, flag);
    }

    public void uploadImageWithFlag(UploadBean uploadBean, int targetWidth, int targetHeight, int flag) {
        uploadImageWithFlag(uploadBean, true, targetWidth, targetHeight, -1, flag);
    }

    public void uploadImage(Bitmap bitmap, boolean compress, int targetWidth, int targetHeight, int maxKB) {
        uploadImageWithFlag(bitmap, compress, targetWidth, targetHeight, maxKB, FLAG_DEFAULT);
    }

    public void uploadImage(UploadBean uploadBean, boolean compress, int targetWidth, int targetHeight, int maxKB) {
        uploadImageWithFlag(uploadBean, compress, targetWidth, targetHeight, maxKB, FLAG_DEFAULT);
    }

    public void uploadImageWithFlag(Bitmap bitmap, boolean compress, int targetWidth, int targetHeight, int maxKB, int flag) {
        uploadImageWithFlag(new UploadBean(bitmap), compress, targetWidth, targetHeight, maxKB, flag);
    }

    public void uploadImageWithFlag(UploadBean uploadBean, boolean compress,
                                    final int targetWidth, final int targetHeight,
                                    final int maxKB, int flag) {
        if (uploadBean == null) return;
        if (!judgeUpdate(uploadBean, flag)) return;

        this.id = uploadBean.id;
        this.flag = flag;
        this.maxKB = maxKB;
        this.compress = compress;
        this.targetWidth = targetWidth;
        this.targetHeight = targetHeight;

        if (!compress) {
            upload(uploadBean);
            return;
        }

        Observable.just(uploadBean)
                .map(new Function<UploadBean, byte[]>() {
                    @Override
                    public byte[] apply(UploadBean uploadBean) {
                        return compressImage(uploadBean, targetWidth, targetHeight, maxKB); // 在io线程中进行图片压缩操作
                    }
                })
                .subscribeOn(Schedulers.io()) // 在io线程中处理图片压缩
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new ObserverFunc2<byte[]>() {
                    @Override
                    public void error(@NotNull Throwable e) {
                        lock = false;
                        if (exception(e)) uploadError(e, null);
                    }

                    @Override
                    public void next(byte[] bytes) {
                        upload(bytes);
                    }
                });
    }

    /**
     * 图片压缩处理
     */
    @NonNull
    protected byte[] compressImage(UploadBean uploadBean, int targetWidth, int targetHeight, int maxKB) {
        if (uploadBean == null) throw new NullPointerException("upload bean is null!");
        if (uploadBean.bitmap != null) {
            return maxKB <= 0 ? bitmapToBytes(zoomBitmap(uploadBean.bitmap, targetWidth, targetHeight))
                    : compressBitmap(uploadBean.bitmap, targetWidth, targetHeight, maxKB);
        }
        if (uploadBean.url != null) {
            return maxKB <= 0 ? bitmapToBytes(zoomBitmap(getLocalBitmap(uploadBean.url), targetWidth, targetHeight))
                    : compressBitmap(getLocalBitmap(uploadBean.url), targetWidth, targetHeight, maxKB);
        }
        throw new NullPointerException("compress image data is null!");
    }

    /**
     * @param uploadBean 上传的本地文件对应的远程链接
     */
    protected void uploadSuccess(@UploadType int type, UploadBean uploadBean) {
        if (sign > 0) {
            sign--;
            lock = false;
            uploadBean.id = id;
            uploadBean.updated = true;
            remoteUploadList.add(uploadBean);
            switch (type) {
                case UploadType.UPLOAD_TYPE_FLAG_FILE:
                    upload(originUploadList.get(sign), flag);
                    break;
                case UploadType.UPLOAD_TYPE_FLAG_IMAGE:
                    uploadImageWithFlag(originUploadList.get(sign), compress, targetWidth, targetHeight, maxKB, flag);
                    break;
            }
            return;
        }
        if (sign == 0) {
            uploadBean.id = id;
            uploadBean.updated = true;
            remoteUploadList.add(uploadBean);
        }
        lock = false;
        sign = -1;
        if (originUploadList.size() > 0 && originUploadList.size() == remoteUploadList.size()) {
            uploadComplete(originUploadList, remoteUploadList, flag);
            return;
        }
        uploadBean.id = id;
        uploadBean.updated = true;
        uploadComplete(originBean, uploadBean, flag);
    }

    protected void uploadFail(Throwable throwable, @Nullable String filePath) {
        lock = false;
        sign = -1;
        uploadError(throwable, filePath);
    }

    private void uploadReady() {
        if (mWeakRefListener == null || mWeakRefListener.get() == null) return;
        mWeakRefListener.get().uploadReady();
    }

    private void uploadError(@Nullable Throwable throwable, @Nullable String filePath) {
        if (mWeakRefListener == null || mWeakRefListener.get() == null) return;
        mWeakRefListener.get().uploadError(throwable, filePath);
    }

    private void uploadComplete(UploadBean originBean, UploadBean remoteBean, int flag) {
        if (mWeakRefListener == null || mWeakRefListener.get() == null) return;
        mWeakRefListener.get().uploadComplete(originBean, remoteBean, flag);
    }

    private void uploadComplete(List<UploadBean> originUploadList, List<UploadBean> remoteUploadList, int flag) {
        if (mWeakRefListener == null || mWeakRefListener.get() == null) return;
        mWeakRefListener.get().uploadComplete(originUploadList, remoteUploadList, flag);
    }

    private WeakReference<OnOssUploadResultListener> mWeakRefListener;

    /**
     * 该接口最好是在{@link Activity#onResume()}中调用
     */
    public void setOnOssUploadResultListener(OnOssUploadResultListener listener) {
        mWeakRefListener = new WeakReference<>(listener);
    }

    protected abstract void upload(@NonNull UploadBean uploadBean);

    protected abstract void upload(@NonNull byte[] uploadBytes);

    protected abstract boolean exception(Throwable throwable);
}
